package com.google.fbyoutube.server;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.google.apphosting.api.DeadlineExceededException;
import com.google.fbyoutube.client.YouTubeAppService;
import com.google.fbyoutube.shared.FBUtility;
import com.google.fbyoutube.shared.FeedUrlMaker;
import com.google.fbyoutube.shared.ytComment;
import com.google.fbyoutube.shared.ytVideoEntry;
import com.google.gdata.client.youtube.YouTubeService;
import com.google.gdata.data.Person;
import com.google.gdata.data.TextContent;
import com.google.gdata.data.extensions.Comments;
import com.google.gdata.data.media.mediarss.MediaContent;
import com.google.gdata.data.media.mediarss.MediaGroup;
import com.google.gdata.data.media.mediarss.MediaThumbnail;
import com.google.gdata.data.media.mediarss.NormalPlayTime;
import com.google.gdata.data.youtube.CommentEntry;
import com.google.gdata.data.youtube.CommentFeed;
import com.google.gdata.data.youtube.VideoEntry;
import com.google.gdata.data.youtube.VideoFeed;
import com.google.gdata.util.AuthenticationException;
import com.google.gdata.util.ServiceException;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * The server side implementation of the RPC service.
 */
public class YouTubeAppServiceImpl extends RemoteServiceServlet implements
		YouTubeAppService {

	public YouTubeAppServiceImpl() {
		logger = Logger.getLogger(this.getClass().getName());
	}

	public YouTubeAppServiceImpl(HttpServletRequest request) {
		this();
		this.request = request;
	}
	
	/**
	 * Interface implementation section
	 */

	@Override
	public List<ytVideoEntry> getTopRateVideos(String cat,
			Map<String, String> options) {
		String feedUrl = TOP_RATED_FEED;
		String region = options.get(FBUtility.YT_REGION);
		if (region != null && FeedUrlMaker.regionSpec.containsKey(region)) {
			feedUrl = STANDARD_FEED_PREFIX + FeedUrlMaker.regionSpec.get(region)
					+ "/top_rated";
			options.remove(FBUtility.YT_REGION);
		}
		feedUrl = appendUrlCategory(cat, feedUrl);
		feedUrl = appendUrlOptions(feedUrl, options);
		return getVideosWithCache(feedUrl);
	}

	public List<ytVideoEntry> getVideos(String feedurl) {
		return getVideosWithCache(feedurl);
	}
	
	@Override
	public List<ytVideoEntry> getMostPopularVideos(String cat,
			Map<String, String> options) {
		
		String feedUrl = MOST_POPULAR_FEED;
		String region = options.get(FBUtility.YT_REGION);
		if (region != null && FeedUrlMaker.regionSpec.containsKey(region)) {
			feedUrl = STANDARD_FEED_PREFIX + FeedUrlMaker.regionSpec.get(region)
					+ "/most_popular";
			options.remove(FBUtility.YT_REGION);
		}
		feedUrl = appendUrlCategory(cat, feedUrl);
		feedUrl = appendUrlOptions(feedUrl, options);
		return getVideosWithCache(feedUrl);
	}

	@Override
	public List<ytVideoEntry> getTopFavoriteVideos(Map<String, String> options) {
		String feedUrl = TOP_FAVORITE_FEED;
		feedUrl = appendUrlOptions(feedUrl, options);
		logger.fine(feedUrl);
		return getVideosWithCache(feedUrl);
	}

	@Override
	public List<ytVideoEntry> getMostViewedVideos(String cat,
			Map<String, String> options) {
		String feedUrl = MOST_VIEWED_FEED;
		String region = options.get(FBUtility.YT_REGION);
		if (region != null && FeedUrlMaker.regionSpec.containsKey(region)) {
			feedUrl = STANDARD_FEED_PREFIX + FeedUrlMaker.regionSpec.get(region)
					+ "/most_viewed";
			options.remove(FBUtility.YT_REGION);
		}
		feedUrl = appendUrlCategory(cat, feedUrl);
		feedUrl = appendUrlOptions(feedUrl, options);
		return getVideosWithCache(feedUrl);
	}

	@Override
	public List<ytVideoEntry> getRecommendationVideos() {
		List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
		String feedUrl = USER_FEED_PREFIX + DEFAULT_USER
				+ RECOMMENDATIONS_FEED_SUFFIX;
		feedUrl = setFeedVersion(feedUrl, 2);

		List<VideoEntry> vodList = fetchVideos(feedUrl);
		if (!standalone) {
		if (vodList != null) {
			for (VideoEntry vod : vodList) {
				ytVideoEntry newVideo = convertVideoEntry(vod);
				returnList.add(newVideo);
			}
		}
		} else {
			for (int i=0; i<20; i++){
				returnList.add(convertVideoEntry(null));
			}
		}

		return returnList;

	}

	@Override
	public List<ytVideoEntry> searchVideos(Map<String, String> options) {
		String feedUrl = VIDEOS_FEED;
		feedUrl = appendUrlOptions(feedUrl, options);
		logger.fine(feedUrl);
		List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
		if (!standalone) {
		List<VideoEntry> vodList = fetchVideos(feedUrl);
		if (vodList != null) {
			for (VideoEntry vod : vodList) {
				ytVideoEntry newVideo = convertVideoEntry(vod);
				returnList.add(newVideo);
			}
		}
		}
		else {
			for (int i=0; i<20; i++){
				returnList.add(convertVideoEntry(null));
			}
		}
		return returnList;
	}
	
	@Override
	public List<ytComment> getTextComment(String vodId) {
		List<ytComment> ytComments = new ArrayList<ytComment>();
		VideoEntry vod = getVideoEntryStatic(vodId);
		Comments comments = vod.getComments();
		if (comments != null && comments.getFeedLink() != null) {
			CommentFeed feed = null;
			try {
				feed = vod.getService().getFeed(
						new URL(comments.getFeedLink().getHref()),
						CommentFeed.class);
			} catch (MalformedURLException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			} catch (ServiceException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}

			if (feed != null) {
				for (CommentEntry e : feed.getEntries()) {
					ytComments.add(convertComment(e));
				}
			}
		}
		return ytComments;
	};

	@Override
	public List<ytVideoEntry> getRelatedVideos(String vodId) {
		// logger.warning(" Enter getRelatedVideos: Current ms: " +
		// String.valueOf(new Date().getTime()));
		List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();

		if (relateVideoCache.get(vodId) != null) {
			// logger.warning(" getRelatedVideos: Find video in cache start + Current ms: "
			// + String.valueOf(new Date().getTime()));
			for (String relateVid : relateVideoCache.get(vodId)) {
				ytVideoEntry ve = getVideoById(relateVid);
				returnList.add(ve);
			}
			// logger.warning(" getRelatedVideos: Find video in cache end + Current ms: "
			// + String.valueOf(new Date().getTime()));
		} else {
			if (!standalone) {
			VideoEntry ve = getVideoEntryStatic(vodId);
			List<VideoEntry> vodList = null;
			if (ve.getRelatedVideosLink() != null) {
				String feedUrl = ve.getRelatedVideosLink().getHref();
				vodList = fetchVideos(feedUrl);
			}
			if (vodList != null) {
				List<String> relatedVids = new ArrayList<String>();
				for (VideoEntry vod : vodList) {
					ytVideoEntry newVideo = convertVideoEntry(vod);
					relatedVids.add(newVideo.getId());
					returnList.add(newVideo);
				}
				relateVideoCache.put(vodId, relatedVids);
			}
			} else {
				for (int i = 0; i < 20; i++){
					returnList.add(convertVideoEntry(null));
				}
			}
		}
		return returnList;
	}

	@Override
	public ytVideoEntry getVideoById(String vodId) {
		if (videoCache.get(vodId) != null) {
			return convertVideoEntry(videoCache.get(vodId));
		}
		String feedUrl = VIDEOS_FEED;
		feedUrl += "/" + vodId;
		VideoEntry vod = fetchVideo(feedUrl);
		if (vod != null || standalone) {
			ytVideoEntry newVideo = convertVideoEntry(vod);
			return newVideo;
		}
		return null;
	}
	
	// End of Interface Implementation section
	
	/**
	 * Cache and utility function on fetched video entries
	 */
	
	private List<ytVideoEntry> getVideosWithCache(final String feedUrl) {
		final List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
		// Search in cache

		List<ytVideoEntry> videos = CacheVideoFeedService.getCachedVideoFeed(feedUrl);
		if (videos != null) {
			returnList.addAll(videos);
		} else {
			logger.warning("Fail to retrieve feed from cache, " + feedUrl);
			// This is not expected to execute, videos should in cache
			if (!standalone) {
				List<VideoEntry> vodList = fetchVideos(feedUrl);
				if (vodList != null) {
					for (VideoEntry vod : vodList) {
						ytVideoEntry newVideo = convertVideoEntry(vod);
						returnList.add(newVideo);
					}
				}
			} else {
				for (int i = 0; i < 20; i++) {
					returnList.add(convertVideoEntry(null));
				}
			}
			CacheVideoFeedService.updateCache(feedUrl, returnList);
		}

		return returnList;
	}

	private String appendUrlCategory(String cat, String feedUrl) {
		if (cat != null && !cat.isEmpty()) {
			feedUrl += "_" + cat;
		}
		return feedUrl;
	}

	private String appendUrlOptions(String feedUrl, Map<String, String> options) {
		feedUrl = setFeedVersion(feedUrl, 2);
		if (options != null) {
			for (String key : options.keySet()) {
				feedUrl += "&" + key + "=" + options.get(key);
			}
		}
		return feedUrl;
	}
	
	private static String setFeedVersion(String feedUrl, int feedVersion) {
		return feedUrl + "?v=" + feedVersion;
	}

	/**
	 * Convert VideoEntry to ytVideoEntry, so that server can pass it to client
	 * through GWT remote service
	 * @param ve - VideoEntry instance that need to convert
	 * @return local ytVideoEntry instance
	 */
	
	public ytVideoEntry convertVideoEntry(VideoEntry ve) {
		ytVideoEntry r = new ytVideoEntry();
		
		// User standalone mode in case app can not connect to YouTube feed server
		if (!standalone) {
			r.setId(ve.getId());
			if (ve.getTitle() != null)
				r.setTitle(ve.getTitle().getPlainText());
			if (ve.getSummary() != null)
				r.setSummaryPlainText(ve.getSummary().getPlainText());

			if (ve.getRating() != null) {
				r.setRateAverage(ve.getRating().getAverage());
			}

			if (ve.getStatistics() != null) {
				r.setViews(ve.getStatistics().getViewCount());
				r.setFavCount(ve.getStatistics().getFavoriteCount());
			}

			if (ve.getAuthors() != null) {
				List<Person> l = ve.getAuthors();
				r.setAuthor(l.get(0).getName());
			}

			MediaGroup mg = ve.getMediaGroup();
			if (mg != null) {
				if (mg.getDescription() != null) {
					r.setMediaGroupDescription(mg.getDescription()
							.getPlainTextContent());
				}
				if (mg.getPlayer() != null) {
					r.setMediaGroupPlayerUrl(mg.getPlayer().getUrl());
				}
				if (mg.getThumbnails() != null) {
					for (MediaThumbnail mt : mg.getThumbnails()) {
						r.getMediaGroupThmbnails().add(mt.getUrl());
						if (r.getMediaGroupRuntime() == null) {
							// The first thumbnail time is the right half of the
							// total runtime
							// so calculate the runtime by double it;
							long runTime = mt.getTime().getTimeOffsetMs() * 2;
							NormalPlayTime playTime = new NormalPlayTime(
									runTime);
							r.setMediaGroupRuntime(playTime
									.getNptHhmmssRepresentation().substring(3));
						}
					}
				}
				if (mg.getContents() != null) {
					for (MediaContent mc : mg.getContents()) {
						r.getMediaGroupContentUrls().add(mc.getUrl());
					}
				}
			}
		} else {

			r.setId("tag:youtube.com,2008:video:degl9zHtFIY");
			r.setTitle("BIRD SECKS!!!");
			r.setSummaryPlainText("This is the summary,  This is the summary, This is the summary.");

			r.setRateAverage(4);

			r.setViews(23123);
			r.setFavCount(93848);

			r.setAuthor("xiaodao");

			r.setMediaGroupDescription("Media group description, ....");
			r.setMediaGroupPlayerUrl("http://www.youtube.com/watch?v=t6CXAaI1OAo&amp;feature=youtube_gdata_player");
			r.getMediaGroupThmbnails().add(
					"http://i.ytimg.com/vi/t6CXAaI1OAo/default.jpg");
			r.getMediaGroupThmbnails().add(
					"http://i.ytimg.com/vi/t6CXAaI1OAo/1.jpg");
			r.getMediaGroupThmbnails().add(
					"http://i.ytimg.com/vi/t6CXAaI1OAo/2.jpg");
			r.getMediaGroupThmbnails().add(
					"http://i.ytimg.com/vi/t6CXAaI1OAo/3.jpg");
			// The first thumbnail time is the right half of the
			// total runtime
			// so calculate the runtime by double it;
			NormalPlayTime playTime = new NormalPlayTime(1300);
			r.setMediaGroupRuntime(playTime.getNptHhmmssRepresentation()
					.substring(3));
			r.getMediaGroupContentUrls()
					.add("rtsp://v6.cache3.c.youtube.com/CiQLENy73wIaGwmGFO0x9yXodRMYDSANFEgGUghzdGFuZGFyZAw=/0/0/0/video.3gp");

		}
		// Save into cache
		if (videoCache.get(r.getId()) == null) {
			videoCache.put(r.getId(), ve);
		}

		return r;
	}
	
	/**
	 * Convert YouTube CommentEntry to ytComment so that GWT service can pass it to client
	 * @param ce - CommentEntry to convert
	 * @return GWT shared ytComment instance
	 */
	
	private ytComment convertComment(CommentEntry ce) {
		ytComment comment = new ytComment(ce.getTitle().getPlainText(),
				((TextContent) ce.getContent()).getContent().getPlainText(), ce.getHtmlLink().getHref());
		comment.setAuthor(ce.getAuthors().get(0).getName());
		comment.setTime(new Date(ce.getUpdated().getValue()));
		return comment;
	}
	
	/**
	 * Fetch video list from YouTube feed server
	 * 
	 * @param feedUrl - YouTube video feed url
	 * @return a list of VideoEntry instances representing YouTube videos
	 */

	public List<VideoEntry> fetchVideos(String feedUrl) {
		VideoFeed videoFeed = null;
		try {
			videoFeed = service.getFeed(new URL(feedUrl), VideoFeed.class);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (ServiceException e) {
			e.printStackTrace();
			return null;
		} catch (DeadlineExceededException e) {
			e.printStackTrace();
			return null;
		} catch (Throwable e) {
			// Temporary workaround for url fetch failed
			e.printStackTrace();
			return null;
		}

		List<VideoEntry> videoEntries = videoFeed.getEntries();
		if (videoEntries == null || videoEntries.size() == 0) {
			return null;
		}
		return videoEntries;
	}
	
	/**
	 * Fetch video from YouTube feed server
	 * 
	 * @param feedUrl - YouTube video feed url
	 * @return VideoEntry instances representing YouTube video
	 */

	public VideoEntry fetchVideo(String feedUrl) {
		VideoEntry videoEntry = null;
		try {
			videoEntry = service.getEntry(new URL(feedUrl), VideoEntry.class);
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		} catch (ServiceException e) {
			// an invalid video ID was used.
			return null;
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}

		return videoEntry;
	}

	public VideoEntry getVideoEntryStatic(String vodId) {
		if (videoCache.get(vodId) != null) {
			return videoCache.get(vodId);
		}
		
		return fetchVideo(VIDEOS_FEED + "/" + vodId);
	}

	@Override
	protected String readContent(HttpServletRequest request)
			throws ServletException, IOException {
		logger = Logger.getLogger(this.getClass().getName());
		this.request = request;
		return super.readContent(request);
	}

	/**
	 * Adds a video as a favorite.
	 * 
	 * @param service
	 *            An authenticated YouTubeService object.
	 * @throws IOException
	 *             Problems reading user input.
	 */
	public boolean addFavorite(String videoID) throws IOException {

		VideoEntry videoEntry = getVideoEntryStatic(videoID);

		if (videoEntry == null) {
			logger.warning("Sorry, the video ID you entered was not valid.\n");
			return false;
		}

		URL feedUrl = new URL(USER_FEED_PREFIX + DEFAULT_USER
				+ FAVORITES_FEED_SUFFIX);

		try {
			service.insert(feedUrl, videoEntry);
		} catch (ServiceException e) {
			logger.warning("Error adding favorite.");
			return false;
		}

		logger.warning("Video favorited.");

		return true;

	}

	/**
	 * Shows a user's favorites and lets the user manipulate them.
	 * 
	 * @param service
	 *            a YouTubeService object.
	 * @throws IOException
	 *             Error sending request or reading the feed.
	 * @throws ServiceException
	 *             If the service is unable to handle the request.
	 */
	public List<ytVideoEntry> getFavorites() {
		List<VideoEntry> videoEntries = fetchVideos(setFeedVersion(
				USER_FEED_PREFIX + DEFAULT_USER + FAVORITES_FEED_SUFFIX, 2));
		List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
		if (videoEntries != null) {
			for (VideoEntry vod : videoEntries) {
				ytVideoEntry newVideo = convertVideoEntry(vod);
				returnList.add(newVideo);
			}
		}
		return returnList;
	}

	@Override
	public boolean addYTFavoriteVideo(String videoID) {

		if (isYouTubeAccountValid()) {
			VideoEntry videoEntry = getVideoEntryStatic(videoID);

			if (videoEntry == null) {
				logger.warning("Sorry, the video ID you entered was not valid.\n");
				return false;
			}

			try {
				URL feedUrl = new URL(USER_FEED_PREFIX + DEFAULT_USER
						+ FAVORITES_FEED_SUFFIX);
				service.insert(feedUrl, videoEntry);
			} catch (ServiceException e) {
				logger.warning("Error adding favorite.");
				return false;
			} catch (MalformedURLException e) {
				e.printStackTrace();
				return false;
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			}

			logger.warning("Video favorited.");

			return true;
		} else {
			return false;
		}
	}

	@Override
	public boolean deleteYTFavoriteVideo(String vodId) {

		if (isYouTubeAccountValid()) {
			try {
				List<VideoEntry> videoEntries = fetchVideos(setFeedVersion(
						USER_FEED_PREFIX + DEFAULT_USER + FAVORITES_FEED_SUFFIX,
						2));
				if (videoEntries != null) {
					for (VideoEntry vod : videoEntries) {
						if (vod.getId().indexOf(vodId) != -1) {
							vod.delete();
							return true;
						}
					}
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (ServiceException e) {
				e.printStackTrace();
			}
			return false;
		} else {
			return false;
		}
	}

	@Override
	public List<ytVideoEntry> getYTFavoriteVideos() {
		if (isYouTubeAccountValid()) {
			List<VideoEntry> videoEntries = fetchVideos(setFeedVersion(
					USER_FEED_PREFIX + DEFAULT_USER + FAVORITES_FEED_SUFFIX, 2));
			List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
			if (videoEntries != null) {
				for (VideoEntry vod : videoEntries) {
					ytVideoEntry newVideo = convertVideoEntry(vod);
					returnList.add(newVideo);
				}
			}
			return returnList;
		} else {
			return null;
		}
	}

	@Override
	public List<ytVideoEntry> getYTUploadVideos(String username) {
		List<VideoEntry> videoEntries = fetchVideos(setFeedVersion(
				USER_FEED_PREFIX + username + UPLOADS_FEED_SUFFIX, 2));
		List<ytVideoEntry> returnList = new ArrayList<ytVideoEntry>();
		if (videoEntries != null) {
			for (VideoEntry vod : videoEntries) {
				ytVideoEntry newVideo = convertVideoEntry(vod);
				returnList.add(newVideo);
			}
		}
		return returnList;
	}

	public boolean authUser(String username, String password) {
		try {
			service.setUserCredentials(username, password);
			return true;
		} catch (AuthenticationException e) {
			HttpSession session = request.getSession(true);
			session.setAttribute(FBUtility.YT_USERNAME, username);
			session.setAttribute(FBUtility.YT_USERPASSWORD, password);
			logger.warning("Invalid login credentials.");
		}
		return false;
	}

	public boolean revokeUser() {
		try {
			service.setUserCredentials(null, null);
			return true;
		} catch (AuthenticationException e) {
			logger.warning("Invalid login credentials.");
		}
		return false;
	}
	
	public boolean isYouTubeAccountValid() {
		HttpSession session = request.getSession(true);

		String username = (String) session.getAttribute(FBUtility.YT_USERNAME);
		String password = (String) session
				.getAttribute(FBUtility.YT_USERPASSWORD);

		if (username != null && password != null && !username.isEmpty()
				&& !password.isEmpty()) {
			try {
				service.setUserCredentials(username, password);
				return true;
			} catch (AuthenticationException e) {
				return false;
			}
		} else {
			return false;
		}
	}


	/**
	 * The name of the server hosting the YouTube GDATA feeds
	 */
	public static final String YOUTUBE_GDATA_SERVER = "http://gdata.youtube.com";

	/**
	 * The prefix common to all standard feeds
	 */
	public static final String STANDARD_FEED_PREFIX = YOUTUBE_GDATA_SERVER
			+ "/feeds/api/standardfeeds/";

	/**
	 * The URL of the "Most Recent" feed
	 */
	public static final String MOST_RECENT_FEED = STANDARD_FEED_PREFIX
			+ "most_recent";

	/**
	 * The URL of the "Most Recent" feed
	 */
	public static final String MOST_POPULAR_FEED = STANDARD_FEED_PREFIX
			+ "most_popular";

	/**
	 * The URL of the "Top Favorites" feed
	 */
	public static final String TOP_FAVORITE_FEED = STANDARD_FEED_PREFIX
			+ "top_favorites";

	/**
	 * The URL of the "Top Rated" feed
	 */
	public static final String TOP_RATED_FEED = STANDARD_FEED_PREFIX
			+ "top_rated";

	/**
	 * The URL of the "Most Viewed" feed
	 */
	public static final String MOST_VIEWED_FEED = STANDARD_FEED_PREFIX
			+ "most_viewed";

	/**
	 * The URL of the "Videos" feed
	 */
	public static final String VIDEOS_FEED = YOUTUBE_GDATA_SERVER
			+ "/feeds/api/videos";

	/**
	 * The prefix of the User Feeds
	 */
	public static final String USER_FEED_PREFIX = YOUTUBE_GDATA_SERVER
			+ "/feeds/api/users/";

	/**
	 * The URL suffix of the test user's uploads feed
	 */
	public static final String UPLOADS_FEED_SUFFIX = "/uploads";

	/**
	 * The URL suffix of the test user's favorites feed
	 */
	public static final String FAVORITES_FEED_SUFFIX = "/favorites";

	/**
	 * The URL suffix of the test user's recommendations feed
	 */
	public static final String RECOMMENDATIONS_FEED_SUFFIX = "/recommendations";

	/**
	 * The URL suffix of the test user's subscriptions feed
	 */
	public static final String SUBSCRIPTIONS_FEED_SUFFIX = "/subscriptions";

	/**
	 * The URL suffix of the test user's playlists feed
	 */
	public static final String PLAYLISTS_FEED_SUFFIX = "/playlists";

	/**
	 * The default username. Used to fetch feed of current auth user.
	 */
	public static final String DEFAULT_USER = "default";
	
	public static boolean standalone = false;

	private static final String DEVELOPER_KEY = "AI39si5_N3e_qceCmzORZKIQ98FLUUjsc9w9oVi_KtUHxLvzFBPbmtutW898LD_ik2X5hrEWyE9iS6OOd34LJfquBOne_jyWUQ";

	private YouTubeService service = new YouTubeService(FBUtility.APP_NAME, DEVELOPER_KEY);
	
	private static final long serialVersionUID = 1L;

	private static Map<String, VideoEntry> videoCache = new HashMap<String, VideoEntry>();

	private static Map<String, List<String>> relateVideoCache = new HashMap<String, List<String>>();

	public static Logger logger;

	public HttpServletRequest request = null;

}
